/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package gp.vlc.rp.net;

import gp.vlc.rp.gui.JDialogException;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.SocketException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import org.apache.http.ConnectionClosedException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseFactory;
import org.apache.http.HttpVersion;
import org.apache.http.auth.AuthScope;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.DefaultHttpServerConnection;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author Namo
 */
public class ReverseProxySocket extends Thread {

    private Logger _logger = LoggerFactory.getLogger(this.getClass());
    private ReverseProxyConfiguration _config;
    private DefaultHttpClient _defaultHttpClient;
    private DefaultHttpServerConnection _defaultServerConnection;

    public ReverseProxySocket(Socket socket, ReverseProxyConfiguration config) throws URISyntaxException, KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, KeyManagementException, UnrecoverableKeyException {
        _config = config;

        _defaultHttpClient = MyFactory.createHttpClient(config);
        _defaultServerConnection = new DefaultHttpServerConnection();
        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        _defaultServerConnection.bind(socket, params);
    }

    @Override
    public void run() {
        try {

            HttpResponseFactory factory = new DefaultHttpResponseFactory();


            // Start worker thread
            while (true) {

                HttpRequest vlcRequest = _defaultServerConnection.receiveRequestHeader();
                if (vlcRequest instanceof HttpEntityEnclosingRequest) {
                    // always consume the optional entity (never know)
                    HttpEntity entity = ((HttpEntityEnclosingRequest) vlcRequest).getEntity();
                    EntityUtils.consume(entity);
                }

                HttpGet serverGet = new HttpGet(_config.getURI());
                serverGet.addHeader(vlcRequest.getFirstHeader("Content-Range"));
                serverGet.addHeader(vlcRequest.getFirstHeader("Range"));
                _logger.info(vlcRequest.toString());
                _logger.info("vlc asked with header " + vlcRequest.getFirstHeader("Range"));
                _logger.info("vlc asked with header " + vlcRequest.getFirstHeader("Content-Range"));

                // received a request; means we should fetch the resource on the server
                HttpResponse serverResponse = _defaultHttpClient.execute(serverGet);

                switch (serverResponse.getStatusLine().getStatusCode()) {
                    case 401:
                        throw new Exception("server authentication failed, check username and password");
                    case 407:
                        throw new Exception("proxy authentication failed, check proxy username and password");
                }

                InputStream serverInputStream = serverResponse.getEntity().getContent();

                final HttpResponse vlcResponse = factory.newHttpResponse(serverResponse.getStatusLine(), null);
                vlcResponse.addHeader(serverResponse.getFirstHeader("Content-Range"));

                _logger.info("server content length is " + serverResponse.getEntity().getContentLength());
                vlcResponse.setEntity(new InputStreamEntity(serverInputStream, serverResponse.getEntity().getContentLength()));
                // let another thread send the response
                new Thread() {
                    public void run() {
                        try {
                            _defaultServerConnection.sendResponseHeader(vlcResponse);
                            _defaultServerConnection.sendResponseEntity(vlcResponse);
                        }
                        catch (Exception e) {
                            _logger.info("response sending thread stopped");
                        }
                    }
                }.start();
            }
        }
        catch (ConnectionClosedException e) {
            _logger.info("normally ended");
            // nothing
        }
        catch (SocketException e) {
            _logger.info("normally ended");
            // nothing
        }
        catch (Throwable e) {
            new JDialogException(e).setVisible(true);
        }
        finally {
            try {
                _defaultServerConnection.close();
            }
            catch (Exception e) {
                // nothing really
            }
            try {
                _defaultHttpClient.getConnectionManager().shutdown();
            }
            catch (Exception e) {
                // nohting to do
            }
        }
    }
}
